package com.term.util.random;


import com.term.util.valuerange.ValueRange;
import com.term.util.valuerange.ValueRangeCollectors;

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * Created by chenGang on 2016/12/29.
 */
public class RandomUtils {

    /**
     * 在upperBound~lowerBoundary生成size个随机数
     * @param size
     * @param upperBound
     * @param lowerBoundary
     * @return
     */
    public static List<Integer> getRandomIntegers(int size, int lowerBoundary, int upperBound){
        Random random = new Random(System.currentTimeMillis());
        return random.ints(size, lowerBoundary, upperBound + 1).boxed().collect(Collectors.toList());
    }

    /**
     * 在1~lowerBoundary生成size个随机数
     * @param size
     * @param upperBound
     * @return
     */
    public static List<Integer> getRandomIntegers(int size, int upperBound){
        Random random = new Random(System.currentTimeMillis());
        return random.ints(size, 1, upperBound + 1).boxed().collect(Collectors.toList());
    }

    /**
     * 在upperBound~lowerBoundary生成1个随机数
     * @param upperBound
     * @param lowerBoundary
     * @return
     */
    public static Integer getRandomInteger(int lowerBoundary, int upperBound){
        Random random = new Random(System.currentTimeMillis());
        return random.ints(1, lowerBoundary, upperBound + 1).boxed().findFirst().get();
    }

    /**
     * 在1~lowerBoundary生成1个随机数
     * @param upperBound
     * @return
     */
    public static Integer getRandomInteger(int upperBound){
        Random random = new Random(System.currentTimeMillis());
        return random.ints(1, 1, upperBound + 1).boxed().findFirst().get();
    }

    /**
     * 从数据集合中随机选size个数据
     * @param datas
     * @param size
     * @param <T>
     * @return
     */
    public static <T> List<T> getRandomList(List<T> datas, int size){
        Random random = new Random(System.currentTimeMillis());
        return random.ints(size, 0, datas.size()).mapToObj(i -> datas.get(i)).collect(Collectors.toList());
    }

    /**
     * 从数据集合中随机选size个数据，且不重复
     * @param datas
     * @param size
     * @param <T>
     * @return
     */
    public static <T> List<T> getNoDuplicateRandomList(List<T> datas, int size){
        // 若超过可选数量，则返回原可选的集合
        if (datas.size() < size) return datas;

        Random random = new Random(System.currentTimeMillis());
        // 随机生成size个随机数字，并去重
        int[] ints = random.ints(size, 0, datas.size()).distinct().toArray();

        // 若当前不满足最终的大小，则再次随机取size个并且和之前的结果进行合并去重，直至超过最终大小
        while (ints.length < size){
            ints = IntStream.concat(Arrays.stream(ints), random.ints(size, 0, datas.size())).distinct().toArray();
        }

        return Arrays.stream(ints).limit(size).mapToObj(i -> datas.get(i)).collect(Collectors.toList());
    }

    /**
     * 从数据集合中随机选1个数据
     * @param datas
     * @param <T>
     * @return
     */
    public static <T> List<T> getRandomSingle(List<T> datas){
        return getRandomList(datas, 1);
    }

    /**
     * 根据对象的概率分布，随机取一个数，并返回选中的对象
     * @param datas
     * @return
     */
    public static Integer getProbabilityRandomInteger(List<Integer> datas) throws Exception {
        return getProbabilityRandom(datas, integer -> integer);
    }

    /**
     * @param datas
     * @param function
     * @param <T>
     * @return
     */
    public static <T> T getProbabilityRandom(List<T> datas, Function<T, Integer> function) throws Exception {
        // 计算当前所有的概率加起来的总数
        int sum = datas.stream().map(function).mapToInt(Integer::intValue).sum();
        if (sum % 100 != 0) throw new Exception("概率分布数据有误");

        // 筛选出一个随机数
        Integer random = getRandomInteger(sum);

        // 将所有的概率转换为区间对象
        List<ValueRange> valueRanges = datas.stream().map(function).collect(ValueRangeCollectors.toList());

        // 从区间对象中找到这个区间
        Integer choose = valueRanges.stream().filter(valueRange -> valueRange.isBetween(random)).findFirst().get().getIndex();

        return datas.get(choose);
    }

    public static void main(String[] args) {
        List<String> noDuplicateRandomList = getNoDuplicateRandomList(Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"), 30);
        noDuplicateRandomList.stream().sorted().forEach(System.out::println);
    }
}
